'use strict';

var _Math = {

    DEG2RAD: Math.PI / 180,
    RAD2DEG: 180 / Math.PI,

    generateUUID: ( function () {
        // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
        var lut = [];

        for ( var i = 0; i < 256; i ++ ) {
            lut[ i ] = ( i < 16 ? '0' : '' ) + ( i ).toString( 16 );
        }

        return function generateUUID() {
            var d0 = Math.random() * 0xffffffff | 0;
            var d1 = Math.random() * 0xffffffff | 0;
            var d2 = Math.random() * 0xffffffff | 0;
            var d3 = Math.random() * 0xffffffff | 0;
            var uuid = lut[ d0 & 0xff ] + lut[ d0 >> 8 & 0xff ] + lut[ d0 >> 16 & 0xff ] + lut[ d0 >> 24 & 0xff ] + '-' +
                lut[ d1 & 0xff ] + lut[ d1 >> 8 & 0xff ] + '-' + lut[ d1 >> 16 & 0x0f | 0x40 ] + lut[ d1 >> 24 & 0xff ] + '-' +
                lut[ d2 & 0x3f | 0x80 ] + lut[ d2 >> 8 & 0xff ] + '-' + lut[ d2 >> 16 & 0xff ] + lut[ d2 >> 24 & 0xff ] +
                lut[ d3 & 0xff ] + lut[ d3 >> 8 & 0xff ] + lut[ d3 >> 16 & 0xff ] + lut[ d3 >> 24 & 0xff ];

            // .toUpperCase() here flattens concatenated strings to save heap memory space.
            return uuid.toUpperCase();
        };
    } )(),

    clamp: function ( value, min, max ) {
        return Math.max( min, Math.min( max, value ) );
    },

    // compute euclidian modulo of m % n
    // https://en.wikipedia.org/wiki/Modulo_operation

    euclideanModulo: function ( n, m ) {
        return ( ( n % m ) + m ) % m;
    },

    // Linear mapping from range <a1, a2> to range <b1, b2>

    mapLinear: function ( x, a1, a2, b1, b2 ) {
        return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
    },

    // https://en.wikipedia.org/wiki/Linear_interpolation

    lerp: function ( x, y, t ) {
        return ( 1 - t ) * x + t * y;
    },

    // http://en.wikipedia.org/wiki/Smoothstep

    smoothstep: function ( x, min, max ) {
        if ( x <= min ) return 0;
        if ( x >= max ) return 1;

        x = ( x - min ) / ( max - min );
        return x * x * ( 3 - 2 * x );
    },

    smootherstep: function ( x, min, max ) {
        if ( x <= min ) return 0;
        if ( x >= max ) return 1;

        x = ( x - min ) / ( max - min );
        return x * x * x * ( x * ( x * 6 - 15 ) + 10 );
    },

    // Random integer from <low, high> interval
    randInt: function ( low, high ) {
        return low + Math.floor( Math.random() * ( high - low + 1 ) );
    },

    // Random float from <low, high> interval
    randFloat: function ( low, high ) {
        return low + Math.random() * ( high - low );
    },

    // Random float from <-range/2, range/2> interval
    randFloatSpread: function ( range ) {
        return range * ( 0.5 - Math.random() );
    },

    degToRad: function ( degrees ) {
        return degrees * _Math.DEG2RAD;
    },

    radToDeg: function ( radians ) {
        return radians * _Math.RAD2DEG;
    },

    isPowerOfTwo: function ( value ) {
        return ( value & ( value - 1 ) ) === 0 && value !== 0;
    },

    ceilPowerOfTwo: function ( value ) {
        return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) );
    },

    floorPowerOfTwo: function ( value ) {
        return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) );
    }
};


function Scene(){

    this.type = 'Scene';
    this.fog = null;
    this.overrideMaterial = null;
    this.uuid = _Math.generateUUID();
    this.name = '';
    this.parent = null;
    this.children = [];
    
    return this;
};

Scene.prototype = {
    add: function ( object ) {

        if ( arguments.length > 1 ) {
            for ( var i = 0; i < arguments.length; i ++ ) {
                this.add( arguments[ i ] );
            }

            return this;
        }

        if ( object === this ) {
            console.error( "Scene.add: object can't be added as a child of itself.", object );
            return this;
        }

        if ( object ) {

            if ( object.parent !== null ) {
                object.parent.remove( object );
            }

            object.parent = this;
            object.dispatchEvent( { type: 'added' } );
            this.children.push( object );
        } else {
            console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object );
        }

        return this;
    },

    remove: function ( object ) {

        if ( arguments.length > 1 ) {
            for ( var i = 0; i < arguments.length; i ++ ) {
                this.remove( arguments[ i ] );
            }

            return this;
        }

        var index = this.children.indexOf( object );

        if ( index !== - 1 ) {
            object.parent = null;
            object.dispatchEvent( { type: 'removed' } );
            this.children.splice( index, 1 );
        }

        return this;
    },

    traverse: function ( callback ) {
        callback( this );

        var children = this.children;
        for ( var i = 0, l = children.length; i < l; i ++ ) {
            children[ i ].traverse( callback );
        }
    },

    getObjectById: function ( id ) {
        return this.getObjectByProperty( 'id', id );
    },

    getObjectByProperty: function ( name, value ) {

        if ( this[ name ] === value ) return this;

        for ( var i = 0, l = this.children.length; i < l; i ++ ) {
            var child = this.children[ i ];
            var object = child.getObjectByProperty( name, value );

            if ( object !== undefined ) {
                return object;
            }
        }

        return undefined;
    },

    toJSON: function ( meta ) {
        var data = {}//Object3D.prototype.toJSON.call( this, meta );
        return data;
    }
}

module.exports.Scene = Scene;